﻿using System;
using Debug = System.Diagnostics.Debug;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IO = System.IO;

using Vector3 = SharpDX.Vector3;
using Position3 = SharpDX.Vector3;
using Serial = SharpDX.Serialization;

// WIP Representations of ocean dynamics and effects.

namespace BoatNGoat.SharedLogic.Water
{
    ///<summary>
    /// Represents some kind of water dynamic, such as current, ripples, etc.
    /// </summary>
    public interface Feature
    {
        Vector3 VelocityAt(Position3 p);
        bool Affecting(Position3 p);

        void Update(TimeSpan deltaT);
    }

    /// <summary>
    /// A circulation is a large-scale loop that causes sprite/entities to
    //  tend to move along the flow direction.
    /// </summary>
    class Circulation : Feature
    {
        private readonly List<Position3> _flow = new List<Position3>();

        public Circulation(Ocean o)
        {
        }

        public void AddPoint(Position3 p)
        {
            _flow.Add(p);
        }

        public bool Affecting(Position3 p)
        {
            return false;
        }

        public Vector3 VelocityAt(Position3 p)
        {
            return new Vector3(0.0f, 0.0f, 0.0f);
        }

        public void Update(TimeSpan deltaT)
        {
        }
    }

    /// <summary>
    /// Uniform flow in a single direction.
    /// </summary>
    class GradualFlow : Feature
    {
        private float _speed;
        private Vector3 _direction;

        public GradualFlow(Ocean o)
        {
            _speed = 0.1f;
            _direction = new Vector3(1.0f, 0.0f, 0.0f);
        }

        public bool Affecting(Position3 p)
        {
            return true;
        }

        public Vector3 VelocityAt(Position3 p)
        {
            Debug.Assert(_direction.IsNormalized);
            return _speed * _direction;
        }

        public void Update(TimeSpan deltaT)
        {
        }
    }

    /// <summary>
    /// Binary serialization for watery things.
    /// </summary>
    interface IDataSerializable
    {
        DataMetrics CalculateDataMetrics();
        void ReadStream(IO.BinaryReader r, DataMetrics m);
        void WriteStream(IO.BinaryWriter w, DataMetrics m); 
    }

    /// <summary>
    /// Fixes the scenarios where you want to:
    /// 1. Size - maintaining bi-directional version compatibility.
    /// 2. Add bits of information to the front of the file (skip).
    /// 3. Change format via versioning.
    /// </summary>
    public class DataMetrics
    {
        public ulong size;
        public ulong skip;
        public ulong version;

        public DataMetrics()
        {
            size = 0;
            skip = 0;
            version = 0;
        }
    }

    /// <summary>
    /// A collection of water features.
    /// </summary>
    public class Ocean : IDataSerializable, Feature
    {

        private readonly List<Feature> _features = new List<Feature>();

        public Ocean()
        {
        }

        public void AddFeature(Feature f)
        {
            _features.Add(f);
        }

        /// <summary>Returns the depth at position p in the coordinate system where the surface
        /// of the ocean is z = 0.0 constant and +z is up.</summary>
        public float DepthAt(Position3 p)
        {
            return -100.0f;
        }

        public Vector3 VelocityAt(Position3 p)
        {
            Vector3 result = new Vector3(0.0f, 0.0f, 0.0f);
            foreach (var f in _features)
            {
                if (f.Affecting(p))
                    Vector3.Add(result, f.VelocityAt(p));
            }
            return result;
        }

        public bool Affecting(Position3 p)
        {
            return true;
        }

        public DataMetrics CalculateDataMetrics()
        {
            var m = new DataMetrics();
            return m;
        }

        public void ReadStream(IO.BinaryReader r, DataMetrics m)
        {
            
        }

        public void WriteStream(IO.BinaryWriter w, DataMetrics m)
        {
        }

        public void Update(TimeSpan deltaT)
        {
            foreach (var f in _features)
            {
                f.Update(deltaT);
            }
        }
    }

    /// <summary>
    /// High-level reader.
    /// </summary>
    interface IOceanReader
    {
        bool ReadInto(ref Ocean o);
    }

    // Read ocean files.
    public class TextFileOceanReader : IOceanReader
    {
        IO.FileStream _fs;

        public TextFileOceanReader(IO.FileInfo textFile)
        {
            if (!textFile.Exists)
                throw new ArgumentException("file does not exist", "textFile");

            _fs = textFile.OpenRead();
        }

        public bool ReadInto(ref Ocean o)
        {
            if (!_fs.CanRead)
                return false;

            var s = new Serial.BinarySerializer(_fs, Serial.SerializerMode.Read);

            DataMetrics m = o.CalculateDataMetrics();

            o.ReadStream(s.Reader, m);
            return true;
        }
    }

    public class FauxOceanReader : IOceanReader
    {
        public FauxOceanReader()
        {
        }

        public bool ReadInto(ref Ocean o)
        {
            o.AddFeature(new GradualFlow(o));
            return true;
        }
    }
}
